/**
 * @file	pwrmgr.c
 * @author	chipsea
 * @brief	
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */
#include "sdk_config.h"
#include "rom_sym_def.h"
#include "types.h"
#include "ll_sleep.h"
#include "cst92f2x.h"
#include "string.h"

#include "pwrmgr.h"
#include "error.h"
#include "gpio.h"
#include "log.h"
#include "clock.h"
#include "jump_function.h"

#include "rf_phy_driver.h"

#if(CFG_SLEEP_MODE == PWR_MODE_NO_SLEEP)
#elif(CFG_SLEEP_MODE == PWR_MODE_SLEEP)
#elif(CFG_SLEEP_MODE == PWR_MODE_PWROFF_NO_SLEEP)
#else
#error "CFG_SLEEP_MODE define incorrect"
#endif

//#define CFG_FLASH_ENABLE_DEEP_SLEEP
#ifdef CFG_FLASH_ENABLE_DEEP_SLEEP
#warning "CONFIG FLASH ENABLE DEEP SLEEP !!!"
#endif

typedef struct _pwrmgr_Context_t{
  per_module_t     moudle_id;
  bool         lock;
  pwrmgr_Hdl_t sleep_handler;
  pwrmgr_Hdl_t wakeup_handler;
}pwrmgr_Ctx_t;

static pwrmgr_Ctx_t mCtx[HAL_PWRMGR_TASK_MAX_NUM];
static uint32_t sramRet_config;
static uint32_t s_config_swClk0 = DEF_CLKG_CONFIG_0;
  
uint32_t s_config_swClk1 = DEF_CLKG_CONFIG_1;
uint32_t s_gpio_wakeup_src_group1,s_gpio_wakeup_src_group2;


void hal_pwrmgr_init(void)
{
  memset(&mCtx, 0, sizeof(mCtx));
#if (CFG_SLEEP_MODE == PWR_MODE_NO_SLEEP) || (CFG_SLEEP_MODE == PWR_MODE_PWROFF_NO_SLEEP)
    disableSleep();
#else 
    enableSleep();
#endif
}

int hal_pwrmgr_clk_gate_config(per_module_t module)
{
  if (module < MOD_CP_CPU)
  {
    s_config_swClk0 |= BIT(module);
  }
  else if (module < MOD_PCLK_CACHE)
  {
    s_config_swClk1 |= BIT(module - MOD_CP_CPU);
  }
  return ERR_NONE;
}

bool hal_pwrmgr_is_lock(per_module_t mod)
{
#if (CFG_SLEEP_MODE == PWR_MODE_NO_SLEEP) || (CFG_SLEEP_MODE == PWR_MODE_PWROFF_NO_SLEEP)
    return TRUE;
#else
	int i;
  int ret = FALSE;
  HAL_ENTER_CRITICAL_SECTION();
  for(i = 0; i< HAL_PWRMGR_TASK_MAX_NUM; i++){
    if(mCtx[i].moudle_id == MOD_NONE)
      break;
    if(mCtx[i].moudle_id == mod){
      if(mCtx[i].lock == TRUE)
        ret = TRUE;
      break;
    }
  }
  HAL_EXIT_CRITICAL_SECTION();
  return ret;
#endif
}


int hal_pwrmgr_lock(per_module_t mod)
{
#if (CFG_SLEEP_MODE == PWR_MODE_NO_SLEEP) || (CFG_SLEEP_MODE == PWR_MODE_PWROFF_NO_SLEEP)
    disableSleep();
    return ERR_NONE;
#else
	int i;
  int ret = ERR_NOT_REGISTED;
  HAL_ENTER_CRITICAL_SECTION();
  for(i = 0; i< HAL_PWRMGR_TASK_MAX_NUM; i++){
    if(mCtx[i].moudle_id == MOD_NONE)
      break;
    if(mCtx[i].moudle_id == mod){
      mCtx[i].lock = TRUE;
      disableSleep();
      //LOG("LOCK\n");
      ret = ERR_NONE;
      break;
    }
  }
  HAL_EXIT_CRITICAL_SECTION();
  return ret;
#endif
}

int hal_pwrmgr_unlock(per_module_t mod)
{
#if (CFG_SLEEP_MODE == PWR_MODE_NO_SLEEP) || (CFG_SLEEP_MODE == PWR_MODE_PWROFF_NO_SLEEP)
    disableSleep();
    return ERR_NONE;
#else
  int i, cnt = 0;
  HAL_ENTER_CRITICAL_SECTION();
  for(i = 0; i< HAL_PWRMGR_TASK_MAX_NUM; i++){
    if(mCtx[i].moudle_id == MOD_NONE)
      break;

    if(mCtx[i].moudle_id == mod){
      mCtx[i].lock = FALSE;
    }
    if(mCtx[i].lock)
      cnt ++;
  }
  if(cnt == 0)
    enableSleep();
  else
    disableSleep();
  HAL_EXIT_CRITICAL_SECTION();
	
	//LOG("sleep mode:%d\n", isSleepAllow());
  
  return ERR_NONE;
#endif
}

ErrCode_t hal_pwrmgr_register(per_module_t mod, pwrmgr_Hdl_t sleepHandle, pwrmgr_Hdl_t wakeupHandle)
{
  int i;
  pwrmgr_Ctx_t* pctx = NULL;
  for(i = 0; i< HAL_PWRMGR_TASK_MAX_NUM; i++){
    if(mCtx[i].moudle_id == mod)
      return ERR_INVALID_STATE;
    if(mCtx[i].moudle_id == MOD_NONE){
      pctx = &mCtx[i];
      break;
    }
  }
  if(i == HAL_PWRMGR_TASK_MAX_NUM)while(1);
    // return ERR_NO_MEM;
  pctx->lock = false;
  pctx->moudle_id = mod;
  pctx->sleep_handler = sleepHandle;
  pctx->wakeup_handler = wakeupHandle;
  return ERR_NONE;
}

ErrCode_t hal_pwrmgr_unregister(per_module_t mod)
{
  int i;
  pwrmgr_Ctx_t* pctx = NULL;
  for(i = 0; i< HAL_PWRMGR_TASK_MAX_NUM; i++)
  {
    if(mCtx[i].moudle_id == mod)
    {
      pctx = &mCtx[i];
      break;
    }
    else if(mCtx[i].moudle_id == MOD_NONE)
    {
      return ERR_NOT_REGISTED;
    }
  }

  if(pctx == NULL)
    return ERR_NOT_REGISTED;

  HAL_ENTER_CRITICAL_SECTION();
  memcpy(pctx, pctx+1, sizeof(pwrmgr_Ctx_t)*(HAL_PWRMGR_TASK_MAX_NUM-i-1));
  HAL_EXIT_CRITICAL_SECTION();
  return ERR_NONE;
}


int __attribute__((used)) hal_pwrmgr_wakeup_process(void)
{
  int i;

#ifdef CFG_FLASH_ENABLE_DEEP_SLEEP
    extern void spif_release_deep_sleep(void);
    spif_release_deep_sleep();
    WaitRTCCount(8);
#endif
  
  AP_PCR->SW_CLK  = s_config_swClk0;
  AP_PCR->SW_CLK1 = s_config_swClk1|0x01;//force set M0 CPU

  s_gpio_wakeup_src_group1 = AP_AON->GPIO_WAKEUP_SRC[0];
  s_gpio_wakeup_src_group2 = AP_AON->GPIO_WAKEUP_SRC[1];
    
  //restore BB TIMER IRQ_PRIO
    NVIC_SetPriority((IRQn_Type)BB_IRQn,    IRQ_PRIO_REALTIME);
    NVIC_SetPriority((IRQn_Type)TIM1_IRQn,  IRQ_PRIO_HIGH);     //ll_EVT
    NVIC_SetPriority((IRQn_Type)TIM2_IRQn,  IRQ_PRIO_HIGH);     //OSAL_TICK
    NVIC_SetPriority((IRQn_Type)TIM4_IRQn,  IRQ_PRIO_HIGH);     //LL_EXA_ADV

  for(i = 0; i< HAL_PWRMGR_TASK_MAX_NUM; i++){
    if(mCtx[i].moudle_id == MOD_NONE){
      return ERR_NOT_REGISTED;
    }
    if(mCtx[i].wakeup_handler)
      mCtx[i].wakeup_handler();
  }
  return ERR_NONE;
}

int __attribute__((used)) hal_pwrmgr_sleep_process(void)
{
  int i;
  hal_pwrmgr_RAM_retention_set();
    //LOG("Sleep\n");
  for(i = 0; i< HAL_PWRMGR_TASK_MAX_NUM; i++){
    if(mCtx[i].moudle_id == MOD_NONE){
      //return ERR_NOT_REGISTED;
      //found last module
      break;
    }
    if(mCtx[i].sleep_handler)
      mCtx[i].sleep_handler();
  }

#ifdef CFG_FLASH_ENABLE_DEEP_SLEEP
    extern void spif_set_deep_sleep(void);
    spif_set_deep_sleep();
#endif
  
  return ERR_NONE;
}

/**************************************************************************************
 * @fn          hal_pwrmgr_RAM_retention
 *
 * @brief       This function process for enable retention sram
 *
 * input parameters
 *
 * @param       uint32_t sram: sram bit map
 *
 * output parameters
 *
 * @param       None.
 *
 * @return      refer error.h.
 **************************************************************************************/
int hal_pwrmgr_RAM_retention(uint32_t sram)
{
  if(sram & 0xffffffe0)
  {
    sramRet_config = 0x00;
    return ERR_INVALID_PARAM;
  } 

  sramRet_config = sram;
  return ERR_NONE;
}

int hal_pwrmgr_RAM_retention_clr(void)
{
  subWriteReg(0x4000f01c,21,17,0x00);
  return ERR_NONE;
}

int hal_pwrmgr_RAM_retention_set(void)
{
  subWriteReg(0x4000f01c,21,17,sramRet_config);
  return ERR_NONE;
}

int hal_pwrmgr_LowCurrentLdo_enable(void)
{
    subWriteReg(0x4000f014,26,26, 1);
    return ERR_NONE;
}

int hal_pwrmgr_LowCurrentLdo_disable(void)
{
    subWriteReg(0x4000f014,26,26, 0);
    return ERR_NONE;
}


extern void gpio_wakeup_set(GpioPin_t pin, gpio_polarity_e type);
extern void gpio_pull_set(GpioPin_t pin, gpio_pupd_e type);
extern void GpioSleepHandler(void);

__attribute__((section("_section_standby_code_"))) void hal_pwrmgr_poweroff(pwroff_cfg_t* pcfg, uint8_t wakeup_pin_num)
{ 
    HAL_ENTER_CRITICAL_SECTION();
    subWriteReg(0x4000f01c,6,6,0x00);   //disable software control
    
    GpioSleepHandler();
    
    for(uint8_t i=0; i<wakeup_pin_num; i++)
    {
        if(pcfg[i].type==POL_FALLING)
            gpio_pull_set(pcfg[i].pin ,GPIO_PULL_UP_S);
        else
            gpio_pull_set(pcfg[i].pin,GPIO_PULL_DOWN);
        gpio_wakeup_set(pcfg[i].pin, pcfg[i].type);
    }
    
    /**
    *  config reset casue as RSTC_OFF_MODE
    *  reset path walkaround dwc
    */
    AON_CLEAR_XTAL_TRACKING_AND_CALIB;
    AP_AON->SLEEP_R[0] = 2;
//    write_reg(0x4000f000,0x5a5aa5a5);
    enter_sleep_off_mode(SYSTEM_OFF_MODE);
    while(1);

}

__attribute__((section("_section_standby_code_"))) void hal_pwrmgr_enter_sleep_rtc_reset(uint32_t sleepRtcTick)
{
    HAL_ENTER_CRITICAL_SECTION();
    subWriteReg(0x4000f01c,6,6,0x00);   //disable software control
    config_RTC(sleepRtcTick);
    // clear sram0 and sram1 retention
//    hal_pwrmgr_RAM_retention_clr();
    subWriteReg(0x4000f01c,21,17,0x04);
    /**
        config reset casue as RSTC_WARM_NDWC
        reset path walkaround dwc
    */
    AON_CLEAR_XTAL_TRACKING_AND_CALIB;
    AP_AON->SLEEP_R[0]=4;
    enter_sleep_off_mode(SYSTEM_SLEEP_MODE);

    while(1) {};
}

#define STANDBY_WAIT_MS(a)  WaitRTCCount((a)<<5) // 32us * 32  around 1ms
/*__attribute__((section("_section_standby_code_"))) */pwroff_cfg_t s_pwroff_cfg[WAKEUP_PIN_MAX]; 
/*__attribute__((section("_section_standby_code_"))) */__attribute__((used)) uint8 pwroff_register_number=0;
__attribute__((section("_section_standby_code_"))) void wakeupProcess_standby(void)
{
    subWriteReg(0x4000f014,29,27,0x07);
    STANDBY_WAIT_MS(5);
#ifdef CFG_FLASH_ENABLE_DEEP_SLEEP
    extern void spif_release_deep_sleep(void);
    spif_release_deep_sleep();
    STANDBY_WAIT_MS(15);
#endif
    uint32_t volatile cnt=0;
    uint8_t volatile find_flag=0;
    uint8 pin_n=0;
    extern bool gpio_read(GpioPin_t pin);
	
	for(pin_n=0; pin_n<pwroff_register_number; pin_n++)
    {
        if((s_pwroff_cfg[pin_n].pin == P2) || (s_pwroff_cfg[pin_n].pin == P3))
        {
            HalGpioPin2Pin3Control(s_pwroff_cfg[pin_n].pin,1);
        }
    }

    for(pin_n=0; pin_n<pwroff_register_number; pin_n++)
    {
        if(gpio_read(s_pwroff_cfg[pin_n].pin)==s_pwroff_cfg[pin_n].type)
        {
            find_flag=1;
            break;
        }
    }

    while(1)
    {
        if(gpio_read(s_pwroff_cfg[pin_n].pin)==s_pwroff_cfg[pin_n].type&&find_flag==1)
        {
            cnt++;
            STANDBY_WAIT_MS(32);

            if(cnt>(s_pwroff_cfg[pin_n].on_time>>5))
            {
                write_reg(0x4000f030, 0x01);
                break;
            }
        }
        else
            hal_pwrmgr_enter_standby(&s_pwroff_cfg[0],pwroff_register_number);
    }
		JUMP_FUNCTION(WAKEUP_PROCESS) = 0;
		wakeupProcess0();
//    set_sleep_flag(0);
//    HAL_ENTER_CRITICAL_SECTION(); 
//    AP_PCR->SW_RESET1 = 0;  
//    while(1);
}
extern void gpio_wakeup_set(GpioPin_t pin, gpio_polarity_e type);
extern void gpio_pull_set(GpioPin_t pin, gpio_pupd_e type);
__attribute__((section("_section_standby_code_"))) void hal_pwrmgr_enter_standby(pwroff_cfg_t* pcfg,uint8_t wakeup_pin_num)
{
    HAL_ENTER_CRITICAL_SECTION();
    subWriteReg(0x4000f01c,6,6,0x00);   //disable software control,Hardware automatically controls power on/off of 32M RCOSC, 32K RCOSC, 32K XOSC, Bandgap, DIGLDO, LCLDO, RTC, LPCMP.
    uint8_t i = 0;

    if(wakeup_pin_num>WAKEUP_PIN_MAX)
    {
        wakeup_pin_num=WAKEUP_PIN_MAX;
    }
    GpioSleepHandler();
    pwroff_register_number = wakeup_pin_num;
    for(i = 0; i < wakeup_pin_num; i++)
    {
        if(pcfg[i].type==POL_FALLING)
            gpio_pull_set(pcfg[i].pin,GPIO_PULL_UP_S);
        else
            gpio_pull_set(pcfg[i].pin,GPIO_PULL_DOWN);

        gpio_wakeup_set(pcfg[i].pin, pcfg[i].type);
        osal_memcpy(&s_pwroff_cfg[i],&(pcfg[i]),sizeof(pwroff_cfg_t));
    }

    JUMP_FUNCTION(WAKEUP_PROCESS)=   (uint32_t)&wakeupProcess_standby;

#ifdef CFG_FLASH_ENABLE_DEEP_SLEEP
    extern void spif_set_deep_sleep(void);
    spif_set_deep_sleep();
    WaitRTCCount(50);
#endif

    subWriteReg(0x4000f014,29,27,0);
    set_sleep_flag(1);
    AP_AON->SLEEP_R[0] = 2;
    subWriteReg(0x4000f01c,21,17,sramRet_config);
    enter_sleep_off_mode(SYSTEM_SLEEP_MODE);
    while(1);
}





